#coding=utf-8
from __future__ import with_statement
#   Author:Zhangbo
#   Email:zhangboks@gmail.com  
#   QQ(If you have):513364476

import collections
import errno
import logging
import socket
import sys
import os
import re
import cgi
import functools

try:
    from netMinc import buffer, reactor
except ImportError:
    import init_netminc
    
class BadHTTPRequestError(Exception):
    """ Raise when recvive a bad request"""
    pass

class HTTPApplicationBase(object):
    def __init__(self, request_body):
        self.request_body = request_body
        self.url_route = dict()
        self.settings()
        self.handle_request()

    def add_url_handler(self, url, callback):
        if url and callback:
            self.url_route[url] = callback

    def set_attribute(self, *args, **kwargs):
        pass

    def settings(self):
        pass

    def handle_request(self):
        try:
            for url, cb in self.url_route.iteritems():
                _REGULAR_ = re.compile(url)
                if _REGULAR_.match(self.request_body.url):
                    ret = cb()
                    if ret:
                        self.response(200, "OK", ret)
                    else:
                        self.response(500, "Server Error", "")
                    return
        except:
            logging.error("Url route error of url %s", url)
        self.response(500, "Server Error", "")

    def get_full_url(self):
        return self.request_body.url

    def is_keep_alive(self):
        return self.request_body.header.get('Connection')

    def get_argument(self, key):
        if self.request_body.method in ("POST", "PUT"):
            return self.request_body.arguments.get(key, None)
        return None

    def close(self):
        self.request_body.close()

    def response(self, status_code, status, body):
        self.request_body.write("HTTP/1.1 %d %s\r\nContent-Length: %d\r\n\r\n%s" % (status_code, status, len(body), body))

class HTTPConnection(object):
    def __init__(self, _buffer, Application, is_keep_alive = False, time_out = 200):
        self._buffer = _buffer
        self.application = Application
        self._read_head_data()
        self.request_body = None
        self.time_out = 200
        self.is_keep_alive = is_keep_alive

    def _head_callback(self, http_data):
        body_datas = http_data.find("\r\n")
        head_line = http_data[:body_datas]
        try:
            method, url, version = head_line.split(" ")
        except:
            self._buffer.close()
            raise BadHTTPRequestError("Bad http request in (method, url, version)")
        if not method or not (method.upper() in ("GET", "POST", "DELETE", "HEAD ", "OPTION", "TRACE", "PUT")) or not version.startswith("HTTP/"):
            self._buffer.close()
            raise BadHTTPRequestError("Bad http request in (method, url, version)")
        head_dict = http_parser(http_data[body_datas:])
        if not head_dict:
            self._buffer.close()
            raise BadHTTPRequestError("Bad http request in http_parser")
        self.request_body = Request(http_connection = self, method = method, url = url, version = version, http_head = head_dict)
        content_len = head_dict.get("Content-Length")
        if content_len:
            content_len = int(content_len)
            if content_len > 1024*1024*10:
                raise BadHTTPRequestError("Bad http request, content_length is too long")
            if head_dict.get("Expect") == "100-Continue":
                self._buffer.write("HTTP/1.1 100 (Continue)\r\n\r\n")
            self._buffer.read_until(content_len, self._body_callback)
            return
        self.application(self.request_body)

    def _body_callback(self, http_data):
        self.request_body.body = http_data
        content_type = self.request_body.header.get("Content_Type", "")
        if self.request_body.method in ("POST", "PUT"):
            #Normal enctype of form's default code
            if not content_type.startswith("multipart/"):
                paris = cig.parse_qs(self.request_body.body)
                for key, value in paris.iteritems():
                    values = [v for v in values if v]
                    if values:
                        self.request_body.arguments.setdefault(key, []).extend(values)
            else:
                logging.warning("Warning at multipart/form-data, check POST or PUT method of ")
        self.application(self.request_body)

    def _close_callback(self):
        close_flag = False
        #if server is_keep_alive = False, Close connection when transfied datas 
        if not self.is_keep_alive:
            close_flag = True
        # version 1.1 ,default keep-alive
        if self.request_body.version == "HTTP/1.1":
            try:
                keep_time = self.request_body.header.get("Keep-Alive", 0)
                self._buffer.add_timeout(time.time() + min(self.time_out, int(keep_time)), self._buffer.close)
            except:
                close_flag = True
        else:# version 1.0, default non keep-alive
            close_flag = self.request_body.header.get("Conent-Length", True)
        if close_flag:
            self._buffer.close()
        return

    def _read_head_data(self):
        try:
            self._buffer.read_until("\r\n\r\n", self._head_callback)
        except BadHTTPRequestError:
            logging.warning("Http head parser error")

    def write(self, data):
        self._buffer.write(data, self._close_callback)

    def close(self):
        self._buffer.close()

class Request(object):
    def __init__(self, http_connection, method, url, version, http_head):
        self.connection = http_connection
        self.method = method
        self.url = url
        self.version = version
        self.header = http_head
        self.body = None
        self.arguments = dict()

    def write(self, data):
        self.connection.write(data)

    def close(self):
        self.connection.close()

class HTTPServer(object):
    def __init__(self, Application, is_keep_alive = False):
        self.HTTPApplication = Application
        self.sock = None
        self._reactor = None
        self.is_keep_alive = is_keep_alive

    def connection_ready(self, sock, fd, events):
        con, add = None, None
        while 1:
            try:
                con, add = sock.accept()
            except socket.error, e:
                if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                    return
            if con:
                con.setblocking(0)
                print  con, add
                _Buf = buffer.Buffer(con)
                #create a connection to deal with the busneiss of this connection
                HTTPConnection(_Buf, self.HTTPApplication, is_keep_alive = self.is_keep_alive)
 
    def run(self, port = 80):
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.setblocking(0)
            self.sock.bind(("", port))
            self.sock.listen(512)
        except:
            logging.error("Server start error")

        self._reactor = reactor.Reactor.instance()
        self._reactor.add_handler(self.sock.fileno(), functools.partial(self.connection_ready, self.sock), self._reactor.READEV)
        self._reactor.run()

    def close(self):
        if self.sock:
            self.sock.close()
            self.sock = None
        if self._reactor:
            self._reactor.close()


def http_parser(http_head):
    head_dict = dict()

    def conver_to_stand_http_form(http_data):
        _REGULAR_ = re.compile(r'^[A-Z0-9][a-z0-9]*(-[A-Z0-9][a-z0-9]*)*$')
        if _REGULAR_.match(http_data):
            return http_data
        return "-".join([_h.capitalize() for _h in http_data.split("-")])

    for line in http_head.splitlines():
        if line:
            line = conver_to_stand_http_form(line)
            key, value = line.split(":", 1)
            head_dict[key] = value
    return head_dict


if __name__ == "__main__":

    class APP(HTTPApplicationBase):

        def settings(self):
            self.add_url_handler(r'/', self.main_index)
            
        def main_index(self):
            body = "<html> <head> Hello world </head> </html>"
            return body

    SERVER = HTTPServer(APP)
    SERVER.run(port = 8080)
